/***************************************************************************
 * Open Object Oriented ANSI C                                             *
 ***************************************************************************
 * Code given under the terms of GNU General Public Licence (GPLv3)        *
 * and without any waranties of any kind including but not limitative to   *
 * waranties on merchandability or any particular applications fitness.    *
 * For more information on GNU/GPL go on :                                 *
 * "http://www.gnu.org/copyleft/gpl.html"                                  *
 *                                                                         *
 * You are allowed to use it for any purposes in the terms of GNU/GPLv3    *
 * (or more).                                                              *
 * This text MUST stay in state on copyed sources (or sources using it)    *
 * and must not be altered.                                                *
 * You are allowed to add your name at the end of the Copylefs list.       *
 ***************************************************************************
 * Copylefts List :                                                        *
 ***************************************************************************
 * 2010-03-20 Patrick PIGNOL <pignol_patrick@hotmail.com> (Original)       *
 * (Type your name here if needed)                                         *
 ***************************************************************************/
#include <stdlib.h>
#include <signal.h>
#include "Mem.h"
#include "ExceptionManager.h"
#include "Class.h"
#include "Exception.h"

static ClassVirtualTable* ClassVirtualTableInstance = 0;

Class* ClassNew(unsigned long pArgsCount, va_list* pParameters)
{
    Class* this;
    this = malloc(sizeof(Class));
    if(!this)
    {
        raise(SIGABRT);
    }
    this->VirtualTable = ClassInitialize(this, pArgsCount, pParameters);
    this->Constructor = ClassConstructor;
    this->Destructor = ClassDestructor;
    this->Clone = ClassClone;
    return this;
}

ClassVirtualTable* ClassInitialize(Class* this, unsigned long pArgsCount, va_list* pParameters)
{
    if(!ClassVirtualTableInstance)
    {
        ClassVirtualTableInstance = (ClassVirtualTable*) malloc(sizeof(ClassVirtualTable));
        if(!ClassVirtualTableInstance)
        {
            raise(SIGABRT);
        }

        ClassVirtualTableInstance->TypeID = CLASS_TYPEID;

        ClassVirtualTableInstance->Interface = (ClassInterface*)malloc(sizeof(ClassInterface));
        if(!ClassVirtualTableInstance->Interface)
        {
            raise(SIGABRT);
        }
        ClassVirtualTableInstance->Interface->Constructor = ClassConstructor;
        ClassVirtualTableInstance->Interface->Destructor = ClassDestructor;
        ClassVirtualTableInstance->Interface->Clone = ClassClone;

        ClassVirtualTableInstance->AncestorTable = 0;
        ClassVirtualTableInstance->ReferenceCount = 0;
        ClassVirtualTableInstance->new = ClassNew;
        ClassVirtualTableInstance->Initialize = ClassInitialize;
        ClassVirtualTableInstance->Garbage = ClassGarbage;
        ClassVirtualTableInstance->TypeOf = ClassTypeOf;
        ClassVirtualTableInstance->IsSameTypeThanTypeID = ClassIsSameTypeThanTypeID;
        ClassVirtualTableInstance->IsKindOfTypeID = ClassIsKindOfTypeID;
        ClassVirtualTableInstance->IsSameTypeThan = ClassIsSameTypeThan;
        ClassVirtualTableInstance->IsKindOf = ClassIsKindOf;
    }
    ClassVirtualTableInstance->Interface->Constructor(this, pArgsCount, pParameters);
    ClassVirtualTableInstance->ReferenceCount++;
    return ClassVirtualTableInstance;
}

void ClassGarbage(Class* this)
{
    ClassVirtualTable* vVirtualTable;
    ClassVirtualTable* vTempVirtualTable;
    this->Destructor(this);
    vVirtualTable = this->VirtualTable->AncestorTable;
    while(vVirtualTable)
    {
        vVirtualTable->Interface->Destructor(this);
        vVirtualTable = vVirtualTable->AncestorTable;
    }
    vVirtualTable = this->VirtualTable;
    while(vVirtualTable)
    {
        vVirtualTable->ReferenceCount--;
        vTempVirtualTable = vVirtualTable;
        vVirtualTable = vVirtualTable->AncestorTable;
        if(vTempVirtualTable->ReferenceCount)
        {
            break;
        }
        else
        {
            if(vTempVirtualTable->Interface)
            {
                free(vTempVirtualTable->Interface);
                vTempVirtualTable->Interface = 0;
            }
            free(vTempVirtualTable);
        }
    }
    free(this);
    this = 0;
}

unsigned long int ClassTypeOf(Class* this)
{
    return this->VirtualTable->TypeID;
}

bool ClassIsSameTypeThanTypeID(Class* this, unsigned long int pTypeID)
{
    if(this->VirtualTable->TypeID == pTypeID)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool ClassIsKindOfTypeID(Class* this, unsigned long int pTypeID)
{
    ClassVirtualTable* vVirtualTable = this->VirtualTable;
    while(vVirtualTable)
    {
        if(vVirtualTable->TypeID == pTypeID)
        {
            return true;
        }
        vVirtualTable = vVirtualTable->AncestorTable;
    }
    return false;
}

bool ClassIsSameTypeThan(Class* this, Class* pSource)
{
    return this->VirtualTable->IsSameTypeThanTypeID(this, pSource->VirtualTable->TypeOf(pSource));
}

bool ClassIsKindOf(Class* this, Class* pSource)
{
    return this->VirtualTable->IsKindOfTypeID(this, pSource->VirtualTable->TypeOf(pSource));
}

void ClassConstructor(Class* this, unsigned long int pArgsCount, va_list* pParameters)
{
}

void ClassDestructor(Class* this)
{
}

Class* ClassClone(Class* this)
{
    Class* vClone = 0;
    if(this)
    {
        vClone = new((void* (*)(unsigned long int, va_list*))this->VirtualTable->new, 0);
    }
    return vClone;
}

